﻿Option Strict On
Partial Public Class Window
    Public Class WindowContent
        Protected Friend _WindowClientBounds As Rectangle
        Public ReadOnly Property WindowClientBounds As Rectangle
            Get
                Return _WindowClientBounds
            End Get
        End Property

        Public Property WordSeperator As Char = " "c

        Private _Text As String
        Public Property Text As String
            Get
                Return _Text
            End Get
            Set(value As String)
                If Not _Text = value Then
                    _Text = value
                    UpdateContents()
                End If
            End Set
        End Property

        Private _Lines As New List(Of Line)
        Public Property Lines As IEnumerable(Of Line)
            Get
                Return _Lines
            End Get
            Set(value As IEnumerable(Of Line))
                _Text = String.Join(ControlChars.NewLine, value)
                UpdateContents()
            End Set
        End Property

        Private _Words As New List(Of Word)
        Public ReadOnly Property Words As IEnumerable(Of Word)
            Get
                Return _Words
            End Get
        End Property

        Private _WordsPerLine As New List(Of Integer)
        Public ReadOnly Property WordsPerLine As IEnumerable(Of Integer)
            Get
                Return _WordsPerLine
            End Get
        End Property

        Public ReadOnly Property TextLength As Integer
            Get
                If Not String.IsNullOrEmpty(_Text) Then Return _Text.Length
                Return 0
            End Get
        End Property

        Public ReadOnly Property LineCount As Integer
            Get
                If _Lines IsNot Nothing Then Return _Lines.Count
                Return 0
            End Get
        End Property

        Public ReadOnly Property WordCount As Integer
            Get
                If _Words IsNot Nothing Then Return _Words.Count
                Return 0
            End Get
        End Property

        Public Function GetLineText(line As Line) As String
            If line Is Nothing Then Return String.Empty
            Return _Text.Substring(line.TextPosition, line.LineLength)
        End Function

        Public Function GetWordText(word As Word) As String
            Return _Text.Substring(word.TextPosition, word.WordLength)
        End Function

        Protected Friend Sub UpdateContents()
            Dim line As New Text.StringBuilder
            Dim width As Integer = _WindowClientBounds.Width
            Dim wordCount, wordIndex, textIndex, lineIndex, spaceDelta, subLineIndex As Integer
            Dim sourceLines = _Text.Split({ControlChars.Lf}, StringSplitOptions.None).ToList
            _Lines.Clear()
            _Words.Clear()
            _WordsPerLine.Clear()
            For subLineIndex = 0 To sourceLines.Count - 1
                Dim subline As String = sourceLines(subLineIndex).Trim(ControlChars.Cr)
                Dim words() As String = subline.Split(WordSeperator)
                wordCount = 0 'count words per line

                For i As Integer = 0 To words.Length - 1
                    Dim wordText As String = words(i)
                    spaceDelta = 0
                    If line.Length + 1 + wordText.Length > width AndAlso line.Length > 0 Then
                        _Lines.Add(New Line(lineIndex, textIndex - line.Length, line.Length))
                        _WordsPerLine.Add(wordCount)
                        line.Length = 0
                        wordCount = 0
                        lineIndex += 1
                    End If
                    If i > 0 Then
                        line.Append(WordSeperator)
                        spaceDelta = 1
                    End If
                    line.Append(wordText)

                    _Words.Add(New Word(wordText.Length + spaceDelta, wordIndex, textIndex, lineIndex, line.Length - wordText.Length - spaceDelta))
                    wordIndex += 1
                    textIndex += wordText.Length + spaceDelta
                Next
                If line.Length > 0 OrElse wordCount = 0 Then
                    If subLineIndex < sourceLines.Count - 1 Then
                        line.Append(ControlChars.NewLine)
                        textIndex += 2 'add crlf if not first line
                    End If
                    _Lines.Add(New Line(lineIndex, textIndex - line.Length, line.Length))
                    _WordsPerLine.Add(wordCount)
                    line.Length = 0
                End If
                lineIndex += 1
            Next
        End Sub
    End Class

    Public Class Word
        Public ReadOnly TextPosition As Integer
        Public ReadOnly Line As Integer
        Public ReadOnly LinePosition As Integer
        Public ReadOnly WordIndex As Integer
        Public ReadOnly WordLength As Integer
        Protected Friend Sub New(length As Integer, index As Integer, positionInText As Integer, lineNumber As Integer, positionInLine As Integer)
            WordLength = length
            TextPosition = positionInText
            Line = lineNumber
            LinePosition = positionInLine
            WordIndex = index
        End Sub
    End Class

    Public Class Line
        Public ReadOnly TextPosition As Integer
        Public ReadOnly LineIndex As Integer
        Public ReadOnly Property LineLength As Integer
        Protected Friend Sub New(index As Integer, position As Integer, length As Integer)
            TextPosition = position
            LineIndex = index
            LineLength = length
        End Sub
        Protected Friend Sub IncreaseLineLength(amount As Integer)
            _LineLength += amount
        End Sub
    End Class
End Class
